import pytest
from pytestqt.qt_compat import qt_api
import random
import string

from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import QApplication, QDialogButtonBox
from PyQt5.QtSql import QSqlRecord

from RBK.Model.db import Database
from RBK.View.MainWindow import MainWindow
from RBK.View.NewDialog import NewDialog
from RBK.Model.Functions import runQuery, isError


def generateTestData():
    return ''.join(random.choice(string.ascii_letters) \
                    for i in range(random.randrange(32)))


@pytest.fixture(scope='module', params=['Struct', 'Data'])
def tabletypes(request):
    return request.param

@pytest.fixture(scope='module', params=[(0, 'Actions'), \
                                        (1, 'Currencies'), \
                                        (2, 'Character') \
                                       ])
def datatypes(request):
    return request.param


@pytest.fixture(scope='module', params=['Name', \
                                        'Rulebook PDF Destination', \
                                        'Character Sheet Field' \
                                        ])
def dataFieldIndices(request):
    return request.param


@pytest.fixture(scope='module', params=[('Field Name', ('Name', \
                                                'Rulebook PDF Destination', \
                                                'Character Sheet Field')),
                                         ('Type', 'Text'), \
                                         ('Constraints', 'maxLength(50)'), \
                                         ('User Editable?', 'False') \
                                        ])
def structFieldIndices(request):
    return request.param

@pytest.fixture(scope='module')
def coreRulebook(db):
    cr = runQuery(db.rulebooks, ('SELECT * FROM Rulebooks WHERE GameName = "Tests" AND Core = "True"'))
    if not isError(cr):
        print('CoreRulebook results:')
        print(cr.getValue())
        return cr.getValue()[0]
    else:
        qt_api.qError('Unable to find a core Rulebook.  If you have not run \
                    the NewCoreRulebook test yet, please run that test first.')
        return QSqlRecord()


@pytest.fixture(scope='module')
def game(coreRulebook):
    return coreRulebook.value('GameName')

@pytest.fixture(scope='module')
def version(coreRulebook):
    return coreRulebook.value('GameVersion')

@pytest.fixture(scope='module')
def rbk():
    return generateTestData()

@pytest.fixture(scope='module')
def core():
    return 'False'

@pytest.fixture(scope='module')
def corerbk(coreRulebook):
    return coreRulebook.value('RulebookName')

@pytest.fixture(scope='module')
def corehash(coreRulebook):
    return coreRulebook.value('Hash')

@pytest.fixture(scope='module')
def nonCoreDBName(game, version, rbk):
    return 'Rulebooks/' + game + '/' + version + '/' + rbk + '.rbk'

@pytest.fixture(scope='module')
def coreDBName(game, version, corerbk):
    return 'Rulebooks/' + game + '/' + version + '/' + corerbk + '.rbk'


@pytest.fixture(scope='module')
def db():
    database = Database()
    yield database
    database.closeRulebook()


@pytest.fixture
def window(db):
    window = MainWindow(db)
    return window


def newDialogTitle(bot, win):
    assert win.newDialog.windowTitle() == 'New Rulebook...'
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Cancel), \
                   Qt.LeftButton)


def newDialogGameDefault(bot, win):
    assert win.newDialog.game.currentText() == 'New Game...'
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Cancel), \
                   Qt.LeftButton)


def newDialogGameExists(bot, win, g):
    assert win.newDialog.game.findText(g) > -1
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Cancel), \
                   Qt.LeftButton)


def newDialogNewGameEmpty(bot, win):
    assert not win.newDialog.gamenew.text()
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Cancel), \
                   Qt.LeftButton)


def newDialogVersionDefault(bot, win, g):
    win.newDialog.game.setCurrentText(g)
    assert win.newDialog.version.currentText() == 'New Version...'
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Cancel), \
                   Qt.LeftButton)



def newDialogVersionExists(bot, win, g, v):
    win.newDialog.game.setCurrentText(g)
    assert win.newDialog.version.findText(v) > -1
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Cancel), \
                   Qt.LeftButton)


def newDialogNewVersionEmpty(bot, win):
    assert not win.newDialog.versionnew.text()
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Cancel), \
                   Qt.LeftButton)


def newDialogRuleBookNameDefault(bot, win):
    assert not win.newDialog.rbkname.text()
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Cancel), \
                   Qt.LeftButton)


def newDialogCoreCheckboxDefault(bot, win):
    assert win.newDialog.core.isChecked()
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Cancel), \
                   Qt.LeftButton)


def newDialogCoreRulebookDefault(bot, win):
    assert not win.newDialog.corerbk.currentText()
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Cancel), \
                   Qt.LeftButton)


def newDialogCoreRulebookExists(bot, win, g, v, c):
    win.newDialog.game.setCurrentText(g)
    win.newDialog.version.setCurrentText(v)
    win.newDialog.core.setCheckState(False)
    assert win.newDialog.corerbk.findText(c) > -1
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Cancel), \
                   Qt.LeftButton)


def newRulebook(bot, win, g, v, r, cr):
    if win.newDialog.game.findText(g) > -1:
        bot.keyClicks(win.newDialog.game, g)
    else:
        bot.keyClicks(win.newDialog.gamenew, g)

    if win.newDialog.version.findText(v) > -1:
        bot.keyClicks(win.newDialog.version, v)
    else:
        bot.keyClicks(win.newDialog.versionnew, v)

    bot.keyClicks(win.newDialog.rbkname, r)

    win.newDialog.core.setCheckState(Qt.Unchecked)
    win.newDialog.corerbk.setCurrentText(cr)

    assert True
    bot.mouseClick(win.newDialog.buttons.button(QDialogButtonBox.Ok), \
                   Qt.LeftButton)

@pytest.fixture(scope='module', \
                params=[newDialogTitle,
                        newDialogGameDefault, newDialogGameExists, newDialogNewGameEmpty, 
                        newDialogVersionDefault, newDialogVersionExists, newDialogNewVersionEmpty,
                        newDialogRuleBookNameDefault, newDialogCoreCheckboxDefault,
                        newDialogCoreRulebookDefault, newDialogCoreRulebookExists,
                        newRulebook])
def test(request):
    return request.param


# newDialog is meant to be accepted/rejected by each test, so returned, not yielded.
def test_newDialogTests(qtbot, window, game, version, rbk, corerbk, test):
    def pauseTest():
        while window.newDialog is None:
        #while not window.newDialog.isVisible():
            QApplication.processEvents()
        if(test==newRulebook):
            test(qtbot, window, game, version, rbk, corerbk)
        elif(test==newDialogGameExists or test==newDialogVersionDefault):
            test(qtbot, window, game)
        elif(test==newDialogVersionExists):
            test(qtbot, window, game, version)
        elif(test==newDialogCoreRulebookExists):
            test(qtbot, window, game, version, corerbk)
        else:
            test(qtbot, window)

    window.show()
    qtbot.addWidget(window)
    QTimer.singleShot(500, pauseTest)
    qtbot.keyClick(window, Qt.Key_N, Qt.ControlModifier, delay=1)


##########################
#
# Rulebook Table
#
##########################

def test_RulebookTable(db):
        assert 'Rulebook' in db.models

def test_RulebookHasGameName(db, game):
    gamename = db.models['Rulebook'].record(0).value('GameName')
    assert gamename == game

def test_RulebookHasGameVersion(db, version):
    gameversion = db.models['Rulebook'].record(0).value('GameVersion')
    assert gameversion == version

def test_RulebookHasRulebookName(db, rbk):
    gamerbkname = db.models['Rulebook'].record(0).value('RulebookName')
    assert gamerbkname == rbk

# For Core, should be True
def test_RulebookHasCore(db, core):
    iamcore = db.models['Rulebook'].record(0).value('Core')
    assert iamcore == core

# For Core, should be empty string
def test_RulebookHasCoreRulebook(db, corerbk):
    gamecorerbk = db.models['Rulebook'].record(0).value('CoreRulebook')
    assert gamecorerbk == corerbk

# For Core, should be empty string
def test_RulebookHasCoreRulebookHash(db, corehash):
    gamecorehash = db.models['Rulebook'].record(0).value('CoreRulebookHash')
    assert gamecorehash == corehash

##########################
#
# DataTypes Table
#
##########################

def test_DataTypesTableExists(db, datatypes):
    assert 'DataTypes' in db.models

def test_DataTypeFound(db, datatypes):
    assert db.models['DataTypes'].record(datatypes[0]).value('DataType') == \
                                                                  datatypes[1]


##########################
#
# Struct Tables
#
##########################

def tablename(tt, dt):
    return tt + ' ' + dt

def test_OtherTableExists(db, tabletypes, datatypes):
    assert tablename(tabletypes, datatypes[1]) in db.models

def test_StructFromCoreRulebook(db, datatypes, coreDBName):
    assert db.models[tablename('Struct', \
              datatypes[1])].database().databaseName() == coreDBName

def test_StructColumnCount(db, datatypes):
    assert db.models[tablename('Struct', datatypes[1])].columnCount() == 4

def test_StructRowCount(db, datatypes):
    assert db.models[tablename('Struct', datatypes[1])].rowCount() == 3

def test_StructNameFieldExists(db, datatypes, structFieldIndices):
    assert db.models[tablename('Struct', \
              datatypes[1])].fieldIndex(structFieldIndices[0]) > -1

def test_RowsExists(db, datatypes, structFieldIndices):
    if structFieldIndices[0]=='Field Name':
        assert db.models[tablename('Struct', datatypes[1])].record( \
                        datatypes[0]).value(structFieldIndices[0]) == \
                        structFieldIndices[1][datatypes[0]]
    else:
        assert db.models[tablename('Struct', datatypes[1])].record( \
                        datatypes[0]).value(structFieldIndices[0]) == \
                        structFieldIndices[1]


##########################
#
# Data Tables
#
##########################

def test_DataFromNonCoreRulebook(db, datatypes, nonCoreDBName):
    assert db.models[tablename('Data', \
              datatypes[1])].database().databaseName() == nonCoreDBName

def test_DataColumnCount(db, datatypes):
    assert db.models[tablename('Data', datatypes[1])].columnCount() == 3

def test_DataRowcount(db, datatypes):
    assert db.models[tablename('Data', datatypes[1])].rowCount() == 0

def test_DataNameFieldExists(db, datatypes, dataFieldIndices):
    assert db.models[tablename('Data', datatypes[1])].fieldIndex( \
                                                        dataFieldIndices) > -1
